package org.kisekiproject.course {
	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.display.LoaderInfo;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.ErrorEvent;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.MouseEvent;
	import flash.events.SecurityErrorEvent;
	import flash.events.UncaughtErrorEvent;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.system.ApplicationDomain;
	
	import org.kisekiproject.log.Log;
	import org.kisekiproject.log.LogWindow;
	import org.kisekiproject.persistance.IPersister;
	import org.kisekiproject.persistance.ScormPersister;
	import org.kisekiproject.persistance.SharedObjectPersister;
	import org.kisekiproject.ui.ErrorDialog;
	import org.kisekiproject.ui.UnsavedIcon;

	/**
	 * Runs a course.
	 */
	public class Course extends Sprite {

		private var _courseModel:CourseModel;
		private var _xmlLoader:URLLoader;
		private var _logWindow:LogWindow;
		private var _courseHolder:DisplayObject;
		private var _unsavedIcon:UnsavedIcon;

		/**
		 * Constructor.
		 */
		public function Course() {
			_logWindow=new LogWindow();
			Log.addLogger(_logWindow);

			_courseModel=new CourseModel();
			_courseModel.addEventListener(ErrorEvent.ERROR,onCourseModelError);
		}

		/**
		 * Course error.
		 */
		protected function onCourseModelError(event:ErrorEvent):void {
			var errorDialog:ErrorDialog=new ErrorDialog(new Error(event.text));
			
			addChild(errorDialog);
		}
		
		/**
		 * Uncaught error.
		 */
		protected function onUncaughtError(event:UncaughtErrorEvent):void {
			var errorDialog:ErrorDialog=new ErrorDialog(event.error);
			
			addChild(errorDialog);
		}
		
		/**
		 * Set persister.
		 */
		public function set persister(value:IPersister):void {
			_courseModel.persister=value;
		}

		/**
		 * Use local library?
		 */
		public function set useLocalLibrary(value:Boolean):void {
			_courseModel.useLocalLibrary=value;
		}

		/**
		 * Use a local XML as source and run the course.
		 * Will also enable the local library as class library.
		 */
		private function runXml(xml:XML):void {
			_courseModel.addEventListener(Event.COMPLETE,onCourseParseComplete);

			Log.debug("cache prevention: "+root.loaderInfo.parameters.preventcache);
			if (root.loaderInfo.parameters.preventcache)
				_courseModel.preventCache=true;

			_courseModel.parse(xml);
		}

		/**
		 * Create persister by type.
		 */
		private function createPersister(type:String):IPersister {
			switch (type) {
				case "scorm":
					return new ScormPersister();
					break;

				case "so":
					return new SharedObjectPersister();
					break;

				case "none":
				case null:
					return null;
					break;

				default:
					throw new Error("Unknown persistance mode: "+type);
					break;
			}
		}

		/**
		 * Course parse complete.
		 */
		private function onCourseParseComplete(e:Event):void {
			if (_courseModel.config.debug) {
				addChild(_logWindow);
			}

			if (root.loaderInfo.parameters.persistence) {
				_courseModel.persister=createPersister(root.loaderInfo.parameters.persistence);
			}

			else {
				if (_courseModel.persister && _courseModel.config.persistence)
					throw new Error("Config may not specify persister, it is already set");

				_courseModel.persister=createPersister(_courseModel.config.persistence);
			}

			_unsavedIcon=new UnsavedIcon();
			_unsavedIcon.mouseEnabled=true;
			_unsavedIcon.mouseChildren=false;
			_unsavedIcon.buttonMode=true;
			_unsavedIcon.addEventListener(MouseEvent.CLICK,onUnsavedIconClick);
			addChild(_unsavedIcon);
			_courseModel.unsavedIndicator=_unsavedIcon;

			_courseHolder=_courseModel.displayObject;
			addChildAt(_courseHolder,0);
			updateCoursePosition();
			stage.addEventListener(Event.RESIZE,onStageResize);

			try {
				_courseModel.run();
			}

			catch (e:Error) {
				var errorDialog:ErrorDialog=new ErrorDialog(e);

				addChild(errorDialog);
				//trace("Error: "+e.message+" "+e.getStackTrace());
			}
		}

		/**
		 * Unsaved icon click.
		 */
		protected function onUnsavedIconClick(event:MouseEvent):void {
			_courseModel.store();
		}
		
		/**
		 * Stage resize.
		 */
		protected function onStageResize(event:Event):void {
			updateCoursePosition();
		}
		
		/**
		 * Update position and scale.
		 */
		private function updateCoursePosition():void {
			_courseHolder.y=0;

			var config:CourseConfig=_courseModel.config;

			if (stage.stageWidth>=config.width && stage.stageHeight>=config.height) {
				_courseHolder.scaleX=1;
				_courseHolder.scaleY=1;
				_courseHolder.x=Math.round(stage.stageWidth/2-config.width/2);
			}
				
			else {
				_courseHolder.x=0;
				_courseHolder.y=0;
				
				var xScale:Number=stage.stageWidth/config.width;
				var yScale:Number=stage.stageHeight/config.height;
				var useScale:Number=Math.min(xScale,yScale);
				
				//trace("scale: "+xScale+" "+yScale);
				_courseHolder.scaleX=useScale;
				_courseHolder.scaleY=useScale;
				
				var w:Number=config.width*useScale;
				_courseHolder.x=Math.round(stage.stageWidth/2-w/2);
			}

			_unsavedIcon.x=stage.stageWidth-30;
			_unsavedIcon.y=20;
		}

		/**
		 * Run a course XML source loaded from a url.
		 */
		private function runUrl(url:String):void {
			if (root.loaderInfo.parameters.preventcache)
				url+="?preventcache="+Math.round(Math.random()*1000000000);

			Log.info("running url: "+url);

			_xmlLoader=new URLLoader();
			_xmlLoader.addEventListener(Event.COMPLETE,onXmlLoaderComplete);
			_xmlLoader.addEventListener(IOErrorEvent.IO_ERROR,onXmlLoaderError);
			_xmlLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR,onXmlLoaderError);
			_xmlLoader.load(new URLRequest(url));
		}

		/**
		 * Load error.
		 */
		protected function onXmlLoaderError(event:ErrorEvent):void {
			var errorDialog:ErrorDialog=new ErrorDialog(new Error(event.text));
			
			addChild(errorDialog);
		}

		/**
		 * Xml loaded, go ahead and run the course.
		 */
		protected function onXmlLoaderComplete(event:Event):void {
			var source:XML=new XML(_xmlLoader.data);

			runXml(source);
		}

		/**
		 * Convenience function to run either a url or xml.
		 */
		public function run(arg:Object):void {
			if (!stage)
				throw new Error("Course needs to be added to stage before it can run");

			loaderInfo.uncaughtErrorEvents.addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR,onUncaughtError);

			stage.scaleMode=StageScaleMode.NO_SCALE;
			stage.align=StageAlign.TOP_LEFT;

			if (arg is XML)
				runXml(XML(arg));

			else if (arg is String)
				runUrl(String(arg));

			else
				throw new Error("Argument error");
		}

		/**
		 * Setup stage.
		 */
		private function setupStage():void {
			stage.scaleMode=StageScaleMode.NO_SCALE;
		}
	}
}